package com.yuehuanghun.framework.threadpool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程执行类，让线程在有限的线程池中运行，避免系统资源消耗瞬时过大
 */
public class CommonThreadPool implements LifeCyle, ThreadPool {

	private static final Logger LOGGER = LoggerFactory.getLogger(CommonThreadPool.class);

	private ExecutorService threadPool;

	private int size = 10;

	private LifeCyleStatus status = LifeCyleStatus.NEW;

	private List<ThreadExecutedListener> threadExecutedListenerList = new ArrayList<>();

	private static List<ThreadExecutedListener> defaultThreadExecutedListenerList = new ArrayList<>();

	/**
	 * 一个最大线程数为10的线程池
	 */
	public CommonThreadPool() {
		super();
		threadExecutedListenerList.addAll(defaultThreadExecutedListenerList);
	}

	/**
	 * 一个自定义最大线程数大小的线程池
	 */
	public CommonThreadPool(int poolSize) {
		this();
		if (poolSize < 1) {
			throw new RuntimeException("pool size must bigger than zero!");
		}
		this.size = poolSize;
	}

	@Override
	public void execute(Runnable worker) {

		if (this.status != LifeCyleStatus.RUNNING) {

			this.start();
		}

		this.threadPool.execute(worker);
	}

	@Override
	@PostConstruct
	public void start() {
		LOGGER.info("Start CommonThreadPool......");

		if (this.status != LifeCyleStatus.RUNNING) {
			this.status = LifeCyleStatus.RUNNING;

			ThreadPoolExecutor executor = new ThreadPoolExecutor(1, this.size, 30L, TimeUnit.SECONDS,
					new LinkedBlockingQueue<Runnable>()) {
				public void afterExecute(Runnable r, Throwable t) {
					if (threadExecutedListenerList.isEmpty()) {
						return;
					}
					for (ThreadExecutedListener listener : threadExecutedListenerList) {
						try {
							listener.listen(r, t);
						} catch (Exception e) {
							LOGGER.error("executing threadExecutedListener", e);
						}
					}
				}
			};
			executor.allowCoreThreadTimeOut(true);
			this.threadPool = executor;
		}
	}

	@Override
	@PreDestroy
	public void stop() {
		LOGGER.info("Stopping CommonThreadPool......");

		shutdownAndAwaitTermination(this.threadPool);

		this.threadPool = null;

		LOGGER.info("Stop CommonThreadPool end! ");
	}

	@Override
	public void pause() {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public LifeCyleStatus getStatus() {
		return this.status;
	}

	public void addThreadExecutedListener(ThreadExecutedListener listener) {
		if (listener == null) {
			return;
		}
		this.threadExecutedListenerList.add(listener);
	}

	public static void addDefaultThreadExecutedListeners(ThreadExecutedListener... listeners) {
		for (ThreadExecutedListener listener : listeners) {
			if (listener == null) {
				return;
			}
			defaultThreadExecutedListenerList.add(listener);
		}
	}

	public static void shutdownAndAwaitTermination(ExecutorService pool) {
		pool.shutdown();
		try {
			if (!pool.awaitTermination(4, TimeUnit.SECONDS)) {
				pool.shutdownNow();
				pool.awaitTermination(4, TimeUnit.SECONDS);
			}
		} catch (InterruptedException ie) {
			pool.shutdownNow();
		}
	}

	public static interface ThreadExecutedListener {
		void listen(Runnable runnable, Throwable throwable);
	}
}
